import {
    ref,
    onMounted,
    onBeforeUnmount,
    type Ref,
    watch,
    nextTick,
    watchEffect,
    reactive,
    toRefs,
    createApp,
    type Component,
} from 'vue';

import { getAppEnvironment } from '@/utils/NativeUtils';
import gsap from 'gsap';
import { getUserInfo } from '@/api/main';
import { mock } from 'mockjs';
import i18n from '@/lang/i18nSetup';
import CustomDirectives from '@utils/CustomDirectives';
import { simpleRequest } from '@api/http';
import { useQuery } from '@utils/useRouters';
// import { useWujie } from '@plugins/wujieMicro';

// const { window: win, document: _document } = useWujie();

/**
 * 反向布局给body追加direction='rtl';
 */
export function useRtl(): void {
    const isRtl = ['ar', 'ur'].includes(sessionStorage?.getItem('language'));
    isRtl && (document.body.style.direction = 'rtl');
}

/**
 * 用于获取页面被折叠的高度，可用于控制回到顶部按钮的显隐
 * @params 节流频率，默认300ms
 * @return number 当前页面滚动的px
 */
export function usePageScrolledHeight(throttleTime = 300): Ref<number> {
    let timer = null;
    const scrollHeight = ref(0);

    function handleScroll() {
        if (timer) {
            return;
        }
        timer = setTimeout(() => {
            scrollHeight.value = window.scrollY;
            clearTimeout(timer);
            timer = null;
        }, throttleTime);
    }

    onMounted(() => {
        document.addEventListener('scroll', handleScroll);
    });
    onBeforeUnmount(() => {
        document.removeEventListener('scroll', handleScroll);
    });
    return scrollHeight;
}

/**
 * 当客户端触发分享时，执行回调函数
 * @params callbackFunction 需要执行的回调函数
 * @return Function cancelListen 取消监听函数
 */
type ShareAction =
    | 'ShareToSend'
    | 'ShareToFriends'
    | 'ShareToFacebook'
    | 'ShareToSnapchat'
    | 'ShareToMessage'
    | 'ShareToWhatsapp'
    | 'ShareToCopyLink';

interface ShareCallbackData {
    cancel?: boolean;
    action?: ShareAction;
    h5Data?: {};
    shareUsersNum?: number;
    isOutSide?: boolean; // 是否是端外分享
}

export function useShareCallback(callbackFunction: (data: ShareCallbackData) => any): Function {
    const callback = Symbol.for('shareCallbackFunctions');
    if (!window[callback]) {
        window[callback] = new Map([[callbackFunction, callbackFunction]]);
    } else {
        window[callback].set(callbackFunction, callbackFunction);
    }
    if (!window['handleShareCallback']) {
        window['handleShareCallback'] = (data: ShareCallbackData) => {
            try {
                typeof data === 'string' && (data = JSON.parse(data));
            } catch (e) {}
            if (Object.prototype.toString.call(data) === '[object Object]') {
                data['isOutSide'] = [
                    'ShareToFacebook',
                    'ShareToSnapchat',
                    'ShareToWhatsapp',
                    'ShareToCopyLink',
                    'ShareToMessage',
                ].includes(data?.action || '');
            }
            window[callback].forEach((fn) => {
                fn(Object.prototype.toString.call(data) === '[object Object]' ? data : {});
            });
        };
    }

    // 取消监听
    function cancelListen() {
        window[callback].delete(callbackFunction);
    }

    onBeforeUnmount(() => {
        cancelListen();
    });
    return cancelListen;
}

/**
 * 返回安卓端小键盘唤起状态和屏幕高度px
 * @return [是否弹起键盘(boolean), 键盘高度的px值(number), 取消监听函数(Function)]
 */
export function useAndroidKeyboardStatus(): [Ref<boolean>, Ref<number>, Function] {
    const keyboardCallback = Symbol.for('androidKeyboardCallbackFunctions');
    const isKeyboardPopUp = ref<boolean>(false);
    const keyboardHeightPixel = ref<number>(0);
    const callback = (isShow: boolean, heightPercent: number) => {
        isKeyboardPopUp.value = typeof isShow === 'boolean' ? isShow : false;
        keyboardHeightPixel.value =
            typeof heightPercent === 'number'
                ? Math.floor(heightPercent * document.body.clientHeight)
                : 0;
    };
    if (!window[keyboardCallback]) {
        window[keyboardCallback] = new Map([[callback, callback]]);
    } else {
        window[keyboardCallback].set(callback, callback);
    }
    if (!window['getSoftKeyBoardData']) {
        window['getSoftKeyBoardData'] = (isShown: boolean, heightPercent: number) => {
            window[keyboardCallback].forEach((fn: Function) => fn(isShown, heightPercent));
        };
    }

    // 取消监听
    function cancelListen() {
        window[keyboardCallback].delete(callback);
    }

    onBeforeUnmount(() => {
        cancelListen();
    });
    return [isKeyboardPopUp, keyboardHeightPixel, cancelListen];
}

/**
 * 返回安全区域px值,iOS和部分系统能使用系统浏览器提供的准确数值
 * 系统不支援的，会判断屏幕高度，如果高度大于740，视作全面屏，返回底部75px，顶部0px。
 * @return [顶部安全区域px值(number), 底部安全区域px值(number)]
 */
export function useSafeAreaHeight(): [Ref<number>, Ref<number>] {
    const topSafeAreaHeight = ref<number>(0);
    const bottomSafeAreaHeight = ref<number>(0);

    // 检测是否支持CSS的constant和env函数
    function checkIsSupportAreaCSSProperties(): boolean {
        const topDiv = document.createElement('div');
        topDiv.id = 'check-safe-area-top';
        const topStyles = [
            'position: fixed',
            'z-index: -1',
            'height: constant(safe-area-inset-top)',
            'height: env(safe-area-inset-top)',
        ];
        topDiv.style.cssText = topStyles.join(';');
        document.body.appendChild(topDiv);
        const topDivDom: HTMLElement = document.querySelector('#check-safe-area-top');

        const bottomDiv = document.createElement('div');
        bottomDiv.id = 'check-safe-area-bottom';
        const bottomStyles = [
            'position: fixed',
            'z-index: -1',
            'height: constant(safe-area-inset-bottom)',
            'height: env(safe-area-inset-bottom)',
        ];
        bottomDiv.style.cssText = bottomStyles.join(';');
        document.body.appendChild(bottomDiv);
        const bottomDivDom: HTMLElement = document.querySelector('#check-safe-area-bottom');
        if (topDivDom?.offsetHeight > 0 || bottomDivDom?.offsetHeight > 0) {
            topSafeAreaHeight.value = topDivDom.offsetHeight;
            bottomSafeAreaHeight.value = bottomDivDom.offsetHeight;
            topDivDom.parentNode?.removeChild(topDivDom);
            bottomDivDom.parentNode?.removeChild(bottomDivDom);
            return true;
        }
        topDivDom.parentNode?.removeChild(topDivDom);
        bottomDivDom?.parentNode?.removeChild(bottomDivDom);
        return false;
    }

    onMounted(() => {
        // 系统不支持CSS样式，判断是否全面屏，是的话按75px计算
        if (!checkIsSupportAreaCSSProperties()) {
            const deviceHeight = document.documentElement.clientHeight;
            if (deviceHeight > 740) {
                topSafeAreaHeight.value = 75;
            }
        }
    });
    return [topSafeAreaHeight, bottomSafeAreaHeight];
}

/**
 *  用于给Vant的Sheet标题栏增加手势滑动隐藏功能
 * @param headerDom 标题栏DOM元素(手势响应区域)
 * @param sheetDom Vant的Sheet控件DOM
 * @param isShowRef 控制Sheet控件是否显示的Ref值
 * @param closeCallback 关闭窗口后的回调函数（可选）
 */
export function useVantSheetGestureSliding(
    headerDom: HTMLElement | string,
    sheetDom: HTMLElement | string,
    isShowRef: Ref<boolean>,
    closeCallback?: () => void
): void {
    let header: HTMLElement | null;
    let sheet: HTMLElement | null;
    let touchStartY;
    let defaultTransition;
    const s = Symbol.for('withGesture');

    const unwatch = watch(isShowRef, (v) => {
        if (v) {
            nextTick(() => {
                header =
                    typeof headerDom === 'string' ? document.querySelector(headerDom) : headerDom;
                sheet = typeof sheetDom === 'string' ? document.querySelector(sheetDom) : sheetDom;
                if (header && sheet && !sheet[s] && !header[s]) {
                    defaultTransition = sheet.style.transition;
                    header.addEventListener('touchstart', handleTouchStart);
                    header.addEventListener('touchmove', handleTouchMove);
                    header.addEventListener('touchend', handleTouchEnd);
                    sheet[s] = true;
                    header[s] = true;
                    unwatch();
                }
            });
        }
    });

    onBeforeUnmount(() => {
        header && header.removeEventListener('touchstart', handleTouchStart);
        header && header.removeEventListener('touchmove', handleTouchMove);
        header && header.removeEventListener('touchend', handleTouchEnd);
    });

    function handleTouchStart(event: TouchEvent) {
        event.stopPropagation();
        sheet.style.transition = 'none';
        touchStartY = event.targetTouches[0].clientY;
    }

    function handleTouchMove(event: TouchEvent) {
        event.stopPropagation();
        event.preventDefault();
        if (event.targetTouches[0].clientY > touchStartY) {
            sheet.style.transform = `translateY(${
                (event.targetTouches[0].clientY - touchStartY) / 35
            }rem)`;
        }
    }

    function handleTouchEnd(event: TouchEvent) {
        event.stopPropagation();
        sheet.style.transition = defaultTransition;
        if (event.changedTouches[0].clientY - touchStartY > 120) {
            sheet.style.transform = '';
            isShowRef.value = false;
            closeCallback && closeCallback();
        } else {
            sheet.style.transform = '';
        }
    }
}

/**
 * 监听页面隐藏状态
 * @return isVisible 页面是否隐藏
 */
export function usePageVisibility(): Ref<boolean> {
    const isVisible = ref(true);

    function onVisibilityChange() {
        if (document.hidden) {
            isVisible.value = false;
        } else {
            isVisible.value = true;
        }
    }

    document.addEventListener('visibilitychange', onVisibilityChange);
    onBeforeUnmount(() => {
        document.removeEventListener('visibilitychange', onVisibilityChange);
    });
    return isVisible;
}

/**
 * 获取落地页DeepLink跳转方法和分享数据
 * @return [跳转DeepLink方法, 分享页的自定义数据]
 */
interface ShareData {
    channel?: string;
    data: {
        channel?: string;
        displayUserId?: string;
        h5Data?: string;
        innerCardData?: string;
        redirectUrl?: string;
        shareImageUrl?: string;
        shortLinksId?: string;
        type?: string;
        uri?: string;
        url?: string;
        userId?: string;
    };
    id?: string;
}

export function useShareId(): [() => void, Ref<Record<any, any> | undefined>] {
    try {
        const query = useQuery();
        let shareData: ShareData['data'] = {};
        const customShareData = ref();
        const isFinishedRequest = ref(false);
        const isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
        if (query.shareId) {
            simpleRequest(
                `/configure/api/short-links/${
                    Array.isArray(query.shareId) ? query.shareId[0] : query.shareId
                }`,
                'get',
                {},
                {}
            )().then((res: ShareData) => {
                shareData = res;
                isFinishedRequest.value = true;
                try {
                    customShareData.value = JSON.parse(shareData?.h5Data || '{}');
                } catch (e) {}
            });
        }
        const jumpFunction = () => {
            if (!query.shareId) {
                return;
            }
            if (!isFinishedRequest.value) {
                const unwatch = watch(isFinishedRequest, (f) => {
                    if (f) {
                        unwatch();
                        jumpFunction();
                    }
                });
                return;
            }
            if (!shareData?.redirectUrl) {
                return;
            }
            getAppEnvironment()
                .then((env) => {
                    if (env.toString().toUpperCase() === 'Kumianmei') {
                        try {
                            const { nativeLink } = JSON.parse(shareData?.h5Data || '{}');
                            window.location.href = nativeLink;
                        } catch (e) {
                            // log e
                        }
                    }
                })
                .catch(() => {
                    // 非客户端环境
                    if (isIOS) {
                        window.location.href = shareData?.redirectUrl;
                    } else {
                        window.open(shareData?.redirectUrl);
                    }
                });
        };
        return [jumpFunction, customShareData];
    } catch (e) {
        console.log(e);
        return [null, undefined];
    }
}

/**
 * 返回一个带有滚动效果的数字Ref
 * @params number 原始数字Ref
 * @params duration 过渡时间(单位s)
 * @return 带有动画效果的数字Ref
 */
export function useScrollingNumber(number: Ref<number>, duration = 1): Ref<number> {
    const tweenedNumber = ref(0);
    watchEffect(() => {
        gsap.to(tweenedNumber, {
            duration,
            value: +number.value || 0,
        });
    });
    return tweenedNumber;
}

/**
 * 返回用户的userId、头像和昵称
 */
const myInfo = reactive({
    portrait: '',
    name: '',
    userId: '',
});
let isRefreshed = false;

// 获取用户自己的信息
export function useMyInfo() {
    if (!myInfo.userId) {
        const getUserInfoFromApi = () => {
            getUserInfo().then((res) => {
                isRefreshed = true;
                ({
                    headImgFileUrl: myInfo.portrait,
                    nickname: myInfo.name,
                    userId: myInfo.userId,
                } = res);
            });
        };
        if (localStorage.getItem('userInfo')) {
            try {
                ({
                    headImgFileUrl: myInfo.portrait,
                    nickname: myInfo.name,
                    userId: myInfo.userId,
                } = JSON.parse(localStorage.getItem('userInfo')));
                if (!isRefreshed) {
                    getUserInfoFromApi(); // 取缓存后依然需要再刷新一次用户数据，防止更换了头像或昵称
                }
            } catch (e) {
                getUserInfoFromApi();
            }
        } else {
            getUserInfoFromApi();
        }
    }

    return toRefs(myInfo);
}

/**
 * 快速使用MockJS的一种方法，详见钉钉文档⌈活动项目Mock指南⌋
 * @params requests请求方法对象
 * @params mockData对应请求方法的模拟数据
 */
export function useMock(
    requests: Record<string, Function>,
    mockData: Partial<Record<keyof typeof requests, Function | Object>>
): Record<keyof typeof requests, (...params) => Promise<Record<string, any>>> {
    const mockResult = {};
    Object.keys(requests).forEach((key) => {
        mockResult[key] = (...args) =>
            Promise.resolve(
                mock(
                    typeof mockData[key] === 'function'
                        ? (mockData[key] as Function)(...args)
                        : mockData[key] || {}
                )
            );
    });
    return mockResult;
}

/**
 * 函数式调用组件的一种方法，详见钉钉文档⌈活动项目hooks指南⌋
 * @params comp 需要渲染的组件
 * @params config 传递给组件的props、attrs、emits等
 */
export function useRender(comp: Component, config = {}) {
    const realDOM = document?.createElement('div');
    if (realDOM) {
        const app = createApp(comp, {
            ...config,
            onClosed: () => {
                if (document?.body?.contains?.(realDOM)) {
                    document?.body?.removeChild?.(realDOM);
                }
            },
        });
        document?.body?.insertBefore?.(realDOM, document.body.firstChild);
        const finalComp = app.use(i18n);
        Object.entries(CustomDirectives).forEach(([key, value]) => {
            app.directive(key, value);
        });
        finalComp.mount(realDOM);
    }
}

/**
 * 平滑滚动页面(window.scrollTo在安卓端也可以实现平滑滚动，但iOS不支持，使用该hooks实现双端统一效果)
 * @params targetPositionY 目标滚动位置, 默认为0
 * @params durationTime 持续滚动时间，单位ms，默认为1000ms
 * @params flag 无需传入，用于停止上一次未完成的滚动事件
 */
let scrollFlag = 0;

export function useSmoothScroll(
    targetPositionY = 0,
    durationTime = 1000,
    flag = ++scrollFlag
): void {
    // 缓动函数
    const easeInOut = (time, b, c, d) => {
        // eslint-disable-next-line no-cond-assign
        if ((time /= d / 2) < 1) {
            return (c / 2) * time * time * time + b;
        }
        // eslint-disable-next-line no-return-assign
        return (c / 2) * ((time -= 2) * time * time + 2) + b;
    };
    // 滚动页面
    const scrollPage = (startPosition, change, duration, startTime, fnFlag) => {
        if (fnFlag < scrollFlag) {
            return;
        }
        const position = easeInOut(Date.now() - startTime, startPosition, change, duration);
        document.documentElement.scrollTop = position;
        if (Date.now() - startTime < duration) {
            requestAnimationFrame(() =>
                scrollPage(startPosition, change, duration, startTime, fnFlag)
            );
        }
    };
    scrollPage(
        document.documentElement.scrollTop,
        targetPositionY - document.documentElement.scrollTop,
        durationTime,
        Date.now(),
        flag
    );
}
